<?php
class SeminarWizard_Controller extends WizardController {
    
    public function __construct() {
        parent::__construct(
                'SeminarWizardData', /* data class name */
                '/admin/seminarWizard', /* wizardPath */
                'admin/seminarWizardLayout', /* contentLayout */
                SeminarWizardData::$STEPS
        );
        $this->pageTemplate = 'admin/adminTemplate';
    }

    /* ------------------------------------------------------------------- */
    /* - Seminar                                                         - */
    /* ------------------------------------------------------------------- */
    
    protected function seminarStep() {
        $this->seminarData();

        if ($this->data->seminar) {
            $this->viewData['seminar'] = $this->data->seminar;
        }
        else {
            $this->viewData['seminar'] = array(
	                'language_code' => 'cs',
	                'active' => sql::$FALSE,
	            	'singleDaysAllowed' => sql::$TRUE
            );
        }
    }
    
    protected function seminarData() {
        $this->viewData['languageOptions'] = $this->dao->queryOptions('SELECT code, name FROM language ORDER BY name');
    }
    
    protected function seminarNext() {
        $seminar = $this->postToDbValues();
        $this->viewData['seminar'] = $seminar;

        // Validation
        $validator = new EntityValidator($seminar, 'seminar', $this->msg);
        $validator->required('language_code');
        $validator->required('name');
        $validator->date('begin_date');
        $validator->time('begin_time');
        $validator->date('end_date');
        $validator->time('end_time');
        $validator->required('description');
        $validator->date('registration_deadline');
        
        if (! $this->msg->hasErrors()) {
            // If we have all required fields, compare the beginning and end dateTimes.
            if (date::diffDateTimes($seminar['begin_date'], $seminar['begin_time'],
                    $seminar['end_date'], $seminar['end_time']) <= 0) {
                $this->msg->error('seminarWizard.error-seminarBeginsAfterEnd');
            }
            // Deadline must be before the beginning of the seminar.
            if (date::diffDateTimes($seminar['begin_date'], $seminar['begin_time'],
                    $seminar['end_date'], $seminar['end_time']) <= 0) {
                $this->msg->error('seminarWizard.error-seminarBeginsAfterEnd');
            }
        }
        
        if ($this->msg->hasErrors()) {
            return FALSE;
        }

        // The seminar cannot be active until organizations apply for payment acceptance.
        $seminar['active'] = sql::$FALSE;
        
        $this->data->seminar = $seminar;
    }

    /* ------------------------------------------------------------------- */
    /* - Seminar batches                                                 - */
    /* ------------------------------------------------------------------- */

    protected function seminarBatchStep() {
        $this->seminarBatchData();
    }
    
    protected function seminarBatchData() {
        $this->viewData['seminarBatchTypeOptions'] = $this->dao->queryOptions(
                'SELECT at.code, l10n.name ' .
                'FROM batch_type at ' .
                'JOIN batch_type_l10n l10n ON at.code = l10n.batch_type_code AND l10n.language_code = :language_code ' .
                'ORDER BY name',
                array('language_code' => $this->getLocalLanguageCode()));
        $seminarDefaults = array();
        $this->viewData['seminar'] = array_merge($seminarDefaults, $this->data->seminar);
    }

    public function seminarBatchAdd() {
        $seminarBatch = $this->postToDbValues();

        $this->viewData['seminarBatch'] = $seminarBatch;

        // Validation
        $validator = new EntityValidator($seminarBatch, 'seminarBatch', $this->msg);
        $validator->required('name');
        $validator->date('begin_date');
        $validator->time('begin_time');
        $validator->date('end_date');
        $validator->time('end_time');

        if ($this->data->existsBatch($seminarBatch)) {
            $this->msg->error('seminarBatch.error-alreadyExists', $seminarBatch['name']);
        }

        if (! $this->msg->hasErrors()) {
            // If we have all required fields, compare the beginning and end dateTimes.
            if (date::diffDateTimes($seminarBatch['begin_date'], $seminarBatch['begin_time'],
                    $seminarBatch['end_date'], $seminarBatch['end_time']) <= 0) {
                $this->msg->error('seminarWizard.error-batchBeginsAfterEnd');
            }
            else {
                if (date::diffDateTimes($this->data->seminar['begin_date'], $this->data->seminar['begin_time'],
                        $seminarBatch['begin_date'], $seminarBatch['begin_time']) < 0) {
                    $this->msg->error('seminarWizard.error-batchBeginsBeforeSeminar',
                            format::dateAndTime($this->data->seminar['begin_date'], $this->data->seminar['begin_time']));
                }
                if (date::diffDateTimes($seminarBatch['end_date'], $seminarBatch['end_time'],
                        $this->data->seminar['end_date'], $this->data->seminar['end_time']) < 0) {
                    $this->msg->error('seminarWizard.error-batchEndsAfterSeminar',
                            format::dateAndTime($this->data->seminar['end_date'], $this->data->seminar['end_time']));
                }
            }
        }

        if ($this->msg->hasErrors()) {
            $this->seminarBatchData();
            $this->renderSubview('admin/seminarWizard/seminarBatchStep');
            return;
        }

        // Load the seminar batch type object
        $seminarBatch['batch_type'] = $this->dao->getLocalizedEntityByCode(
                'batch_type', $seminarBatch['batch_type_code'], $this->getL10nLanguageCode());

        $this->data->batchList[] = $seminarBatch;
        $this->msg->info('seminarBatch.createSuccess', $seminarBatch['name']);

        $this->redirect($this->stepUrl);
    }

    public function seminarBatchDelete() {
        $index = $this->input->get('index');

        // Basic validation
        if (! valid::isIndex($index)) {
            $this->msg->error('error.invalidId');
            $this->redirect($this->stepUrl);
            return;
        }

        $batch = $this->data->batchList[$index];
        if ($batch) {
            unset($this->data->batchList[$index]);
        }
        $this->msg->info('seminarBatch.deleteSuccess', $batch['name']);
        $this->redirect($this->stepUrl);
    }

    protected function seminarBatchNext() {
        $seminarBatchForm = $this->postToDbValues();
        $this->viewData['seminarBatchForm'] = $seminarBatchForm;

        if (count($this->data->batchList) == 0) {
            $this->seminarBatchData();
            $this->msg->error('seminarWizard.error-seminarBatchesRequired');
            return FALSE;
        }

        // Put together a used batch type list
        $this->data->usedBatchTypeList = array();
        foreach ($this->data->batchList as $batch) {
            $batchTypeCode = $batch['batch_type_code'];
            if (! array_key_exists($batchTypeCode, $this->data->usedBatchTypeList)) {
                $this->data->usedBatchTypeList[$batchTypeCode] = $batch['batch_type'];
            }
        }
    }

    /* ------------------------------------------------------------------- */
    /* - Price groups                                                    - */
    /* ------------------------------------------------------------------- */

    protected function priceGroupStep() {
        $this->priceGroupData();
    }

    protected function priceGroupData() {
        // If the group list is empty, create the default group and a group for the Czech Republic with its CZK currency.
        if (count($this->data->priceGroupList) == 0) {
            $defaultCurrency = $this->dao->getUnique('currency', array('code' => Kohana::config('yosemin.price_group_default_currency_code')));
            $this->data->priceGroupList[] = array(
                    'name' => text::get('priceGroup.defaultGroup-name'),
                    'currency_code' => $defaultCurrency['code'],
                    'currency' => $defaultCurrency,
                    'country_codes' => NULL,
                    'description' => text::get('priceGroup.defaultGroup-description'),
                    'is_default' => sql::$TRUE
            );

            $czechCurrency = $this->dao->getUnique('currency', array('code' => 'CZK'));

            $this->data->priceGroupList[] = array(
                    'name' => 'Česká republika',
                    'currency_code' => $czechCurrency['code'],
                    'currency' => $czechCurrency,
                    'country_codes' => array('CZ'),
                    'description' => 'Skupina pro účastníky z Česka.',
                    'is_default' => sql::$FALSE
            );
        }

        // Price group name autocomplete values
        $this->viewData['nameAcValues'] = $this->dao->queryFirstValues(
                'SELECT DISTINCT name FROM price_group ORDER BY name ASC');

        // Currencies
        $this->viewData['currencyOptions'] = $this->dao->queryOptions(
                'SELECT code, name, code FROM currency ORDER BY name ASC', NULL, '()');

        // Countries (only those that are not used yet)
        $query = 'SELECT code, name, code FROM country';
        $usedCountryCodes = $this->data->countryCodesInPriceGroups();
        if (count($usedCountryCodes) > 0) {
            $query .= ' WHERE code NOT IN (' . sql::inClauseValues($usedCountryCodes) . ') ';
        }
        $query .= ' ORDER BY name ASC';
        $this->viewData['countryOptions'] = $this->dao->queryOptions($query, NULL, '()');
    }

    public function priceGroupAdd() {
        $priceGroup = $this->postToDbValues();

        $this->viewData['priceGroup'] = $priceGroup;

        // Validation
        $validator = new EntityValidator($priceGroup, 'priceGroup', $this->msg);
        $validator->required('name');
        $validator->required('currency_code');
        $validator->required('country_codes');

        if ($this->data->existsPriceGroup($priceGroup)) {
            $this->msg->error('priceGroup.error-alreadyExists');
        }

        if ($this->msg->hasErrors()) {
            $this->priceGroupData();
            $this->renderSubview('admin/seminarWizard/priceGroupStep');
            return;
        }

        $priceGroup['currency'] = $this->dao->getByCode('currency', $priceGroup['currency_code']);
        $priceGroup['countries'] = $this->dao->queryAll(
                'SELECT * FROM country WHERE code IN (' . sql::inClauseValues($priceGroup['country_codes']) . ')');

        $count = count($this->data->priceGroupList);
        

        $this->data->priceGroupList[] = $priceGroup;
        $this->msg->info('priceGroup.createSuccess', $priceGroup['name']);

        if ($count == count($this->data->priceGroupList)) {
            die ('Pocet: ' . $count);
        }

        $this->redirect($this->stepUrl);
    }

    public function priceGroupDelete() {
        $index = $this->input->get('index');

        // Basic validation
        if (! valid::isIndex($index)) {
            $this->msg->error('error.invalidId');
            $this->redirect($this->stepUrl);
            return;
        }

        $priceGroup = $this->data->priceGroupList[$index];
        if ($priceGroup) {
            unset($this->data->priceGroupList[$index]);
        }
        $this->msg->info('priceGroup.deleteSuccess', $priceGroup['name']);
        $this->redirect($this->stepUrl);
    }

    protected function priceGroupNext() {
    }

    /* ------------------------------------------------------------------- */
    /* - Program types                                                   - */
    /* ------------------------------------------------------------------- */

    protected function programTypeStep() {
        $this->programTypeData();

        if ($this->data->programTypeForm) {
            $this->viewData['programTypeForm'] = $this->data->programTypeForm;
        }
        else {
            $this->viewData['programTypeForm'] = array(
                'batches_of_program_type' => array(
                    'karmayoga' => array_keys($this->data->batchList),
                    'children' => array_keys($this->data->batchList))
            );
        }
        
        if ($this->data->exerciseGroupList) {
            $this->viewData['programTypeForm']['exerciseGroup_codes'] = array();
            foreach ($this->data->exerciseGroupList as $exerciseGroup) {
                $this->viewData['programTypeForm']['exerciseGroup_codes'][] = $exerciseGroup['code'];
            }
        }
        
        if ($this->data->singleDayProgramTypeList) {
        	$this->viewData['programTypeForm']['single_days_program_type_codes'] = arrays::listFromListOfArrays($this->data->singleDayProgramTypeList, 'code');
        }
    }

    protected function programTypeData() {
        $this->viewData['karmayogaTypeOptions'] = $this->optionsFromTypeList('karmayoga');
        
        // Do no select one-day program types
        $programTypeList = $this->dao->findLocalized('program_type', $this->getL10nLanguageCode(), 'code');
        
        $karmayogaAndChildren = array();
        $programTypeCount = count($programTypeList);
        for ($i = $programTypeCount - 1; $i >= 0; $i--) {
        	if ($programTypeList[$i]['single_days_only']) {
        		unset($programTypeList[$i]);
        	}
            else if ($programTypeList[$i]['code'] == 'karmayoga' || $programTypeList[$i]['code'] == 'children') {
                $karmayogaAndChildren[] = $programTypeList[$i];
                unset($programTypeList[$i]);
            }
            else if ($programTypeList[$i]['code'] == 'normal') {
                unset($programTypeList[$i]);
            }
        }
        $this->viewData['programTypeList'] = array_merge($karmayogaAndChildren, $programTypeList);
        
        $this->viewData['singleDayProgramTypeOptions'] = $this->dao->queryOptions(
                'SELECT prog.code, l10n.name, l10n.description ' .
                'FROM program_type prog ' .
                'LEFT OUTER JOIN program_type_l10n l10n ON l10n.program_type_code = prog.code AND l10n.language_code = :language_code ',
                array('language_code' => $this->getL10nLanguageCode()));
        
        $this->viewData['exerciseGroupOptions'] = $this->dao->queryOptions(
                'SELECT code, name ' .
                'FROM exercise_group el ' .
                'LEFT OUTER JOIN exercise_group_l10n l10n ON el.code = l10n.exercise_group_code ' .
                'WHERE l10n.language_code = :language_code ' .
                'ORDER BY name',
                array('language_code' => $this->getLocalLanguageCode()));
    }

    private function optionsFromTypeList($entityName, $codeList=null) {
        $query = 'SELECT ' . $entityName . '_type_code, name FROM ' . $entityName . '_type_l10n types ' .
                'WHERE language_code = :language_code ';
        if ($codeList) {
            $query .= 'AND ' . $entityName . '_type_code IN (' . sql::inClauseValues($codeList) . ') ';
        }
        $query .= 'ORDER BY name';
        return $this->dao->queryOptions($query, array('language_code' => $this->getLocalLanguageCode()));
    }

    protected function programTypeNext() {
        $programTypeForm = $this->postToDbValues();
        $this->viewData['programTypeForm'] = $programTypeForm;

        // karmayoga_type_codes
        // batches_of_program_type[programType.code][seminarBatch.index]

        // Compute karmayoga and children program availability
        $this->data->karmayogaAvailable = is_array($programTypeForm['batches_of_program_type']['karmayoga']) &&
                count($programTypeForm['batches_of_program_type']['karmayoga']) > 0;

        // Basic validation
        if ($this->data->karmayogaAvailable) {
            if (valid::isEmpty($programTypeForm['karmayoga_type_codes'])) {
                $this->msg->error('error.required', 'seminarWizard.karmayoga_type_codes');
            }
        }
        if ($this->msg->hasErrors()) {
            return FALSE;
        }

        // Resolve and read details of all program types that will be available on seminar.
        $availableProgramTypeCodes = array();
        foreach ($programTypeForm['batches_of_program_type'] as $programTypeCode => $batchList) {
            if (count($batchList) > 0) {
                $availableProgramTypeCodes[] = $programTypeCode;
            }
        }
        if (count($availableProgramTypeCodes) > 0) {
            $this->data->availableProgramTypeList = $this->dao->queryAll(
                    'SELECT pt.*, l10n.* ' .
                    'FROM program_type pt ' .
                    'LEFT OUTER JOIN program_type_l10n l10n ON l10n.program_type_code = pt.code AND l10n.language_code = :language_code ' .
                    'WHERE pt.code IN (' . sql::inClauseValues($availableProgramTypeCodes) . ') ' .
                    'ORDER BY name',
                    array('language_code' => $this->getL10nLanguageCode()));
        }
        
        if ($programTypeForm['single_days_program_type_codes']) {
            $this->data->singleDayProgramTypeList = $this->dao->queryAll(
                    'SELECT prog.*, l10n.name, l10n.description ' .
                    'FROM program_type prog ' .
                    'LEFT OUTER JOIN program_type_l10n l10n ON l10n.program_type_code = prog.code AND l10n.language_code = :language_code ' .
                    'WHERE prog.code IN (' . sql::inClauseValues($programTypeForm['single_days_program_type_codes']) . ') ' .
                    'ORDER BY name',
                    array('language_code' => $this->getL10nLanguageCode()));
        }

        $this->data->programTypeForm = $programTypeForm;
        $this->data->karmayogaTypeCodeList = $programTypeForm['karmayoga_type_codes'];
        
        // Exercise groups
        if ($programTypeForm['exerciseGroup_codes']) {
            $this->data->exerciseGroupList = $this->dao->queryAll('SELECT code, name ' .
                    'FROM exercise_group el ' .
                    'LEFT OUTER JOIN exercise_group_l10n l10n ON el.code = l10n.exercise_group_code ' .
                    'WHERE l10n.language_code = :language_code ' .
                    'AND el.code IN (' . sql::inClauseValues($programTypeForm['exerciseGroup_codes']) . ') ' .
                    'ORDER BY name',
                    array('language_code' => $this->getLocalLanguageCode()));
        }
        else {
            $this->data->exerciseGroupList = array();
        }
    }
    
    /* ------------------------------------------------------------------- */
    /* - Accomodation types                                              - */
    /* ------------------------------------------------------------------- */

    protected function accomodationTypeStep() {
        $this->accomodationTypeData();

        $formData = array();
        $this->viewData['accomodationBatch'] = $this->data->accomodationTypeBatchTable;
        
        $formData['singleDayAccommodationType_codes'] = arrays::listFromListOfArrays($this->data->singleDayAccommodationTypeList, 'code', 'name');
        $this->viewData['formData'] = $formData;
    }

    protected function accomodationTypeData() {
        $this->viewData['accomodationTypeList'] = $this->dao->queryAll(
                'SELECT at.code, l10n.* ' .
                'FROM accomodation_type at ' .
                'JOIN accomodation_type_l10n l10n ON at.code = l10n.accomodation_type_code AND l10n.language_code = :language_code ' .
                'ORDER BY order_number',
                array('language_code' => $this->getLocalLanguageCode()));
        
        $this->viewData['singleDayAccommodationTypeOptions'] = $this->dao->queryOptions(
                'SELECT at.code, l10n.name ' .
                'FROM accomodation_type at ' .
                'JOIN accomodation_type_l10n l10n ON at.code = l10n.accomodation_type_code AND l10n.language_code = :language_code ' .
                'ORDER BY order_number',
                array('language_code' => $this->getLocalLanguageCode()));
    }

    protected function accomodationTypeNext() {
        $accomodationTypeForm = $this->postToDbValues();
        $this->viewData['accomodationBatch'] = $accomodationTypeForm['accomodationBatch'];

        $this->accomodationTypeData();

        // Basic validation
        if ($accomodationTypeForm['accomodationBatch'] == NULL) {
            $this->msg->error('error.required', 'seminarWizard.accomodation_type_codes');
        }
        if ($this->data->seminar['singleDaysAllowed']) {
	        if ($accomodationTypeForm['singleDayAccommodationType_codes'] == NULL) {
	            $this->msg->error('error.required', 'seminarWizard.singleDayAccommodationType_codes');
	        }
        }
        if ($this->msg->hasErrors()) {
            return FALSE;
        }

        $availableAccomodationTypeCodes = array_keys($accomodationTypeForm['accomodationBatch']);
        $this->data->availableAccomodationTypeList = $this->dao->queryAll(
                'SELECT at.code, l10n.* ' .
                'FROM accomodation_type at ' .
                'JOIN accomodation_type_l10n l10n ON at.code = l10n.accomodation_type_code AND l10n.language_code = :language_code ' .
                'WHERE at.code IN (' . sql::inClauseValues($availableAccomodationTypeCodes) . ') ' .
                'ORDER BY order_number',
                array('language_code' => $this->getLocalLanguageCode()));

        $this->data->accomodationTypeBatchTable = $accomodationTypeForm['accomodationBatch'];
        
        if ($accomodationTypeForm['singleDayAccommodationType_codes']) {
        	$this->data->singleDayAccommodationTypeList = $this->dao->queryAll(
                'SELECT at.code, l10n.* ' .
                'FROM accomodation_type at ' .
                'JOIN accomodation_type_l10n l10n ON at.code = l10n.accomodation_type_code AND l10n.language_code = :language_code ' .
                'WHERE at.code IN (' . sql::inClauseValues($accomodationTypeForm['singleDayAccommodationType_codes']) . ') ' .
                'ORDER BY order_number',
                array('language_code' => $this->getLocalLanguageCode()));
        }
    }

    function isAccomodationTypeAvailableOnBatchType($accomodationTypeCode, $batchTypeCode) {
        foreach ($this->data->batchList as $batchIndex => $batch) {
            if ($batch['batch_type_code'] == $batchTypeCode) {
                if (array_search($batchIndex, $this->data->accomodationTypeBatchTable[$accomodationTypeCode]) !== FALSE) {
                    return TRUE;
                }
            }
        }
        if ($batchTypeCode == constants::$singleDayBatchTypeCode && $this->data->seminar['singleDaysAllowed']) {
        	foreach ($this->data->singleDayAccommodationTypeList as $accommodationType) {
        		if ($accommodationType['code'] == $accomodationTypeCode) {
        			return TRUE;
        		}
        	}
        	
        }
        return FALSE;
    }

    /* ------------------------------------------------------------------- */
    /* - Prices                                                          - */
    /* ------------------------------------------------------------------- */

    protected function priceStep() {
        $priceForm = array();
        if ($this->data->priceForm) {
            $priceForm = $this->data->priceForm;
        }
        else {
            // TODO: presuň tieto magické hodnoty do konfiguráku
            // Default values
            foreach ($this->data->availableProgramTypeList as $programType) {
                $priceForm['programTypePrice'][$programType['code']] = 100;
            }
            $priceForm['programTypePrice']['karmayoga'] = 50;
        }
        $this->viewData['priceForm'] = $priceForm;

        $this->priceData();
    }

    protected function priceData() {
    	$batchTypeList = $this->data->usedBatchTypeList;
    	if ($this->data->seminar['singleDaysAllowed']) {
	    	if (! array_key_exists(constants::$singleDayBatchTypeCode, $batchTypeList)) {
	    		$batchTypeList[constants::$singleDayBatchTypeCode] = $this->dao->getLocalizedEntityByCode('batch_type', constants::$singleDayBatchTypeCode, $this->getL10nLanguageCode());
	    	}
	    	
    		$this->viewData['singleDayBatchType'] = $this->dao->getLocalizedEntityByCode('batch_type', constants::$singleDayBatchTypeCode, $this->getL10nLanguageCode());
    	}
    	$this->viewData['batchTypeList'] = $batchTypeList;
    }

    protected function priceNext() {
        /* REQUEST VARIABLES
         *
         * accomodationType[accomodationTypeCode][priceGroupIndex] decimal
         * 
         * karmayogaPrice decimal
         * programTypePrice[programType.code]
         * singleDayProgramTypePrice[programType.code][priceGroupIndex]
        */

        $priceForm = $this->postToDbValues();
        $this->viewData['priceForm'] = $priceForm;

        // Validation
        $allPricesFilled = TRUE;
        foreach ($this->data->usedBatchTypeList as $batchType) {
            foreach ($this->data->availableAccomodationTypeList as $accomodationType) {
                if (! $this->isAccomodationTypeAvailableOnBatchType($accomodationType['code'], $batchType['code']) ) {
                    continue;
                }
                foreach ($this->data->priceGroupList as $priceGroupIndex => $priceGroup) {
                    $price = $priceForm['accomodationTypePrice'][$batchType['code']][$accomodationType['code']][$priceGroupIndex];
                    if (valid::isWhite($price)) {
                        $this->msg->error('seminarWizard.error-fillAllPrices');
                        $allPricesFilled = FALSE;
                        break;
                    }
                }
                if (! $allPricesFilled) {
                    break;
                }
            }
        }
        if ($allPricesFilled) {
            foreach ($this->data->usedBatchTypeList as $batchType) {
                foreach ($this->data->availableAccomodationTypeList as $accomodationType) {
                    if (! $this->isAccomodationTypeAvailableOnBatchType($accomodationType['code'], $batchType['code']) ) {
                        continue;
                    }
                    foreach ($this->data->priceGroupList as $priceGroupIndex => $priceGroup) {
                        $price = $priceForm['accomodationTypePrice'][$batchType['code']][$accomodationType['code']][$priceGroupIndex];
                        if (! valid::isPositiveIntegerOrZero($price)) {
                            $this->msg->error('seminarWizard.error-priceNotInteger', $accomodationType['name'], $priceGroup['name']);
                        }
                    }
                }
            }
        }
        
        foreach ($this->data->availableProgramTypeList as $programType) {
			if (valid::isWhite($priceForm['programTypePrice'][$programType['code']])) {
				$this->msg->error('seminarWizard.error-programTypePriceRequired', $programType['name']);
			}
        }
        
        // One day programe prices (satsang and co.)
        $validationError = false;
        foreach ($this->data->singleDayProgramTypeList as $programType) {
        	if (! $programType['single_days_only']) {
        		continue;
        	}
        	foreach ($this->data->priceGroupList as $priceGroupIndex => $priceGroup) {
        		$price = $priceForm['singleDayProgramTypePrice'][$programType['code']][$priceGroupIndex];
        		if (! valid::isPositiveIntegerOrZero($price)) {
        			$this->msg->error('seminarWizard.error-fillAllSingleDayProgramPrices');
        			$validationError = true;
        			break;
        		}
        	}
        	if ($validationError) {
        		break;
        	}
        }
        
        if ($this->msg->hasErrors()) {
            return FALSE;
        }

        $this->data->priceForm = $priceForm;
    }

    /* ------------------------------------------------------------------- */
    /* - Discounts                                                       - */
    /* ------------------------------------------------------------------- */

    protected function discountStep() {
        $discountForm = array();
        if ($this->data->discountForm) {
            $discountForm = $this->data->discountForm;
        }
        else {
            // TODO: presuň tieto magické hodnoty do konfiguráku
            // Default values
            $discountForm['socialBeforeDeadlineOnly'] = sql::$TRUE;
            $discountForm['socialAvailableForKarmayoga'] = sql::$FALSE;
            $discountForm['socialPercentage'] = 5;

            $discountForm['earlyDiscountEnabled'] = sql::$TRUE;
            $discountForm['earlyDiscountAppliesToKarmayoga'] = sql::$FALSE;
            $discountForm['earlyDiscountPercentage'] = 10;
        }
        $this->viewData['discountForm'] = $discountForm;

        $this->discountData();
    }

    protected function discountData() {
    }

    protected function discountNext() {
        /* REQUEST VARIABLES
         *
         * socialBeforeDeadlineOnly (0 | 1)
         * socialAvailableForKarmayoga (0 | 1)
         * socialPercentage
        */

        $discountForm = $this->postToDbValues();
        $this->viewData['discountForm'] = $discountForm;

        // Validation
        if ($discountForm['earlyDiscountEnabled'] ) {
            if (valid::isWhite($discountForm['earlyDiscountPercentage'])) {
                $this->msg->error('error.required', 'seminar.earlyDiscountPercentage');
            }
        }
        if ($discountForm['socialDiscountEnabled'] ) {
            if (valid::isWhite($discountForm['socialPercentage'])) {
                $this->msg->error('error.required', 'seminarWizard.socialPercentage');
            }
        }
        if ($this->msg->hasErrors()) {
            return FALSE;
        }

        $this->data->discountForm = $discountForm;
    }

    /* ------------------------------------------------------------------- */
    /* - Extra charges                                                   - */
    /* ------------------------------------------------------------------- */

    protected function extraChargeStep() {
        $chargesForm = array();
        if ($this->data->chargesForm) {
            $chargesForm = $this->data->chargesForm;
        }
        else {
            // TODO: presuň tieto magické hodnoty do konfiguráku
            // Default values
            $chargesForm['cancellationChargeEnabled'] = sql::$TRUE;
            $chargesForm['cancellationAppliesToKarmayoga'] = sql::$FALSE;
            $chargesForm['cancellationChargeBeforeDeadline'] = 0;
            $chargesForm['cancellationChargeAfterDeadline'] = 10;

        }
        $this->viewData['chargesForm'] = $chargesForm;

        $this->extraChargeData();
    }

    protected function extraChargeData() {
    }

    protected function extraChargeNext() {
        $chargesForm = $this->postToDbValues();
        $this->viewData['chargesForm'] = $chargesForm;

        // Validation
        if ($chargesForm['lateChargeEnabled'] ) {
        	$fieldName = 'lateChargePercentage';
        	if (! valid::isPositiveIntegerOrZero($chargesForm[$fieldName])) {
        		$this->msg->error('error.notPositiveIntegerOrZero', 'seminar.' . $fieldName);
        	}
        }
        
        if ($chargesForm['cancellationChargeEnabled'] ) {
            $fieldName = 'cancellationChargeBeforeDeadline';
            if (valid::isWhite($chargesForm[$fieldName])) {
                $this->msg->error('error.required', 'seminarWizard.' . $fieldName);
            }
            else if (! valid::isPositiveIntegerOrZero($chargesForm[$fieldName])) {
                $this->msg->error('error.notPositiveIntegerOrZero', 'seminarWizard.' . $fieldName);
            }

            $fieldName = 'cancellationChargeAfterDeadline';
            if (valid::isWhite($chargesForm[$fieldName])) {
                $this->msg->error('error.required', 'seminarWizard.' . $fieldName);
            }
            else if (! valid::isPositiveIntegerOrZero($chargesForm[$fieldName])) {
                $this->msg->error('error.notPositiveIntegerOrZero', 'seminarWizard.' . $fieldName);
            }
        }
        if ($chargesForm['payment_on_arrival_fee_czk'] === '' || ! valid::isPositiveIntegerOrZero($chargesForm['payment_on_arrival_fee_czk'])) {
            $this->msg->error('error.notPositiveIntegerOrZero', 'seminar.payment_on_arrival_fee_czk');
        }
        if ($chargesForm['payment_on_arrival_fee_eur'] === '' || ! valid::isPositiveIntegerOrZero($chargesForm['payment_on_arrival_fee_eur'])) {
            $this->msg->error('error.notPositiveIntegerOrZero', 'seminar.payment_on_arrival_fee_eur');
        }
        
        if ($this->msg->hasErrors()) {
            return FALSE;
        }

        // Copy the on arrival payment fees to the seminar object.
        $this->data->seminar['payment_on_arrival_fee_czk'] = $chargesForm['payment_on_arrival_fee_czk'];
        $this->data->seminar['payment_on_arrival_fee_eur'] = $chargesForm['payment_on_arrival_fee_eur'];
        $this->data->chargesForm = $chargesForm;
    }

    /* ------------------------------------------------------------------- */
    /* - Summary                                                         - */
    /* ------------------------------------------------------------------- */

    protected function summaryStep() {
        $this->summaryData();
    }

    protected function summaryData() {
        $this->viewData['seminar'] = $this->data->seminar;
        $this->seminarData();
    }

    protected function summaryNext() {
    }

    /* ------------------------------------------------------------------- */
    /* - Finish                                                          - */
    /* ------------------------------------------------------------------- */

    public function finish() {
        $this->dao->getDb()->beginTransaction();
        try {
            // Save the seminar
            $seminarId = $this->dao->insert('seminar', $this->data->seminar, array('language_code', 'name', 'description', 'singleDaysAllowed'));

            // Insert or update localized names and descriptions
            $seminarL10n = array(
                'seminar_id' => $seminarId,
                'name' => $this->data->seminar['name'],
                'description' => $this->data->seminar['description'],
                'language_code' => $this->data->seminar['language_code']
            );
            $this->dao->insert('seminar_l10n', $seminarL10n);

            // Save the batches
            $batchCodeSequence = 1;
            foreach ($this->data->batchList as $i => $batch) {
                $batch['seminar_id'] = $seminarId;
                $batchCode = $batch['batch_type']['code_part'] . $batchCodeSequence;
                $batch['batch_code'] = $batchCode;
                $this->dao->insert('batch', $batch, array('name', 'description'));
                $this->data->batchList[$i]['batch_code'] = $batchCode;

                // Insert localization
                $this->dao->insert('batch_l10n', array(
	                    'seminar_id' => $seminarId,
	                	'batch_code' => $batchCode,
	                    'language_code' => $this->data->seminar['language_code'],
	                    'name' => $batch['name'],
	                    'description' => $batch['description']
                ));
                $batchCodeSequence ++;
            }
            
            // Generate single day batches
            $singleDayBatchList = array();
            $dateCounter = new DateTime($this->data->seminar['begin_date']);
            $beginDate = $dateCounter->format('Y-m-d');
            $batchCodeSequence = 1;
            if ($this->data->seminar['singleDaysAllowed']) {
            	$sigleDayBatchType = $this->dao->getByCode('batch_type', constants::$singleDayBatchTypeCode);
            	
            	while ($beginDate != $this->data->seminar['end_date']) {
            		$beginDate = $dateCounter->format('Y-m-d');
            		$dateCounter = $dateCounter->add(new DateInterval('P1D'));
            		$endDate = $dateCounter->format('Y-m-d');
            		
            		$batchCode = $sigleDayBatchType['code_part'] . $batchCodeSequence;
            		
	            	$batch = array(
	            			'seminar_id' => $seminarId,
	            			'batch_code' => $batchCode,
	            			'begin_date' => $beginDate,
	            			'end_date' => $endDate,
	            			'begin_time' => $this->data->seminar['begin_time'],
	            			'end_time' => $this->data->seminar['begin_time'],
	            			'batch_type_code' => constants::$singleDayBatchTypeCode
	            	);
	            	$this->dao->insert('batch', $batch);
	            	
	            	// Insert single day translations in languages that will probably be used. 
	            	$languages = array_unique(array($this->data->seminar['language_code'], 'en', $this->getLocalLanguageCode()));
	            	foreach ($languages as $lang) {
		            	$this->dao->insert('batch_l10n', array(
			                    'seminar_id' => $seminarId,
		            			'batch_code' => $batchCode,
			                    'language_code' => $lang,
			                    'name' => text::getInLang($lang, 'seminarBatch-singleDayBatchNamePattern', format::date($beginDate))
		                ));
	            	}
	            	$singleDayBatchList[] = $batch;
	            	$batchCodeSequence ++;
            	}
            }
            

            // Save the price groups
            foreach ($this->data->priceGroupList as $i => $priceGroup) {
                $priceGroup['seminar_id'] = $seminarId;
                $priceGroupId = $this->dao->insert('price_group', $priceGroup, array('currency', 'country_codes'));
                $this->data->priceGroupList[$i]['id'] = $priceGroupId;
                // Save country belonging to this price group
                if ($priceGroup['country_codes'] != NULL) {
                    foreach ($priceGroup['country_codes'] as $countryCode) {
                        $priceGroupCountry = array(
                            'price_group_id' => $priceGroupId,
                            'country_code' => $countryCode
                        );
                        $this->dao->insert('price_group_country', $priceGroupCountry);
                    }
                }
            }

            // Save the program types available in batches
            // Add normal program to all batches
            $this->data->programTypeForm['batches_of_program_type']['normal'] = array_keys($this->data->batchList);
            foreach ($this->data->programTypeForm['batches_of_program_type'] as $programTypeCode => $batchList) {
                foreach ($batchList as $batchIndex) {
                    $availableProgram = array(
							'seminar_id' => $seminarId,
							'batch_code' => $this->data->batchList[$batchIndex]['batch_code'],
							'program_type_code' => $programTypeCode
                    );
                    $this->dao->insert('available_program_type', $availableProgram);
                }
            }
            
            // Save program types available in single day batches except satsang and co.
            foreach ($this->data->singleDayProgramTypeList as $programType) {
            	if ($programType['single_days_only']) {
            		continue;
            	}
            	foreach ($singleDayBatchList as $batch) {
	            	$availableProgram = array(
						'seminar_id' => $seminarId,
						'batch_code' => $batch['batch_code'],
						'program_type_code' => $programType['code']
                    );
                    $this->dao->insert('available_program_type', $availableProgram);
	            }
            }

            // Save the karmayoga types
            if ($this->data->karmayogaAvailable) {
                foreach ($this->data->karmayogaTypeCodeList as $karmayogaTypeCode) {
                    $availableKarmayogaType = array(
                        'seminar_id' => $seminarId,
                        'karmayoga_type_code' => $karmayogaTypeCode
                    );
                    $this->dao->insert('available_karmayoga_type', $availableKarmayogaType);
                }
            }

            // Save the accomodation types
            foreach ($this->data->accomodationTypeBatchTable as $accomodationTypeCode => $batchIndexList) {
                foreach ($batchIndexList as $batchIndex) {
                    $availableAccomodationType = array(
                    		'seminar_id' => $seminarId,
                    		'batch_code' => $this->data->batchList[$batchIndex]['batch_code'],
							'accomodation_type_code' => $accomodationTypeCode
                    );
                    $this->dao->insert('available_accommodation_type', $availableAccomodationType);
                }
            }

            // Save available accommodation types for single days
            foreach ($this->data->singleDayAccommodationTypeList as $accommodationType) {
            	foreach ($singleDayBatchList as $batch) {
            		$availableAccomodationType = array(
            				'seminar_id' => $seminarId,
            				'batch_code' => $batch['batch_code'],
            				'accomodation_type_code' => $accommodationType['code']
            		);
            		$this->dao->insert('available_accommodation_type', $availableAccomodationType);
            	}
            }
            
            // Save available exercise groups
            foreach ($this->data->exerciseGroupList as $i => $exerciseGroup) {
                $availableExerciseGroup = array(
                    'exercise_group_code' => $exerciseGroup['code'],
                    'seminar_id' => $seminarId
                );
                $this->dao->insert('available_exercise_group', $availableExerciseGroup);
            }

            // Save the accomodation prices
            $programTypesWithNormal = $this->data->availableProgramTypeList;
            $programTypesWithNormal[] = $this->dao->getLocalizedEntityByCode(
            		'program_type', constants::$normalProgramTypeCode, $this->getL10nLanguageCode());
            foreach ($this->data->priceForm['accomodationTypePrice'] as $batchTypeCode => $accomodationTypePrice) {
                foreach ($accomodationTypePrice as $accommodationTypeCode => $priceGroupPrice) {
                	foreach ($programTypesWithNormal as $programType) {
                		// Compose a category code
                		$batchType = $this->dao->getByCode('batch_type', $batchTypeCode);
                		$accommodationType = $this->dao->getByCode('accomodation_type', $accommodationTypeCode);
                		$categoryCode = $programType['code_part'] . $batchType['code_part'] . $accommodationType['code_part'];
                		
	                	$category = array(
	                			'seminar_id' => $seminarId,
	                			'accomodation_type_code' => $accommodationTypeCode,
	                			'batch_type_code' => $batchTypeCode,
	                			'program_type_code' => $programType['code'],
	                			'category_code'	=> $categoryCode
	                	);
	                	$this->dao->insert('category', $category);
	                    foreach ($priceGroupPrice as $priceGroupIndex => $price) {
	                    	if ($programType['code'] != constants::$normalProgramTypeCode) {
	                    		// Alternative programs have price set by percantage, so calculate it.
	                    		$price = round($price * $this->data->priceForm['programTypePrice'][$programType['code']] / 100);
	                    	}
	                        $categoryPrice = array(
	                            'seminar_id' => $seminarId,
	                            'price_group_id' => $this->data->priceGroupList[$priceGroupIndex]['id'],
	                            'category_code' => $categoryCode,
	                            'price' => $price
	                        );
	                        $this->dao->insert('category_price', $categoryPrice);
	                    }
                	}
                }
            }
            
            // Save single day program prices
            if ($this->data->priceForm['singleDayProgramTypePrice']) {
            	$batchType = $this->dao->getByCode('batch_type', constants::$singleDayBatchTypeCode);
	            foreach ($this->data->priceForm['singleDayProgramTypePrice'] as $programTypeCode => $prices) {
	            	$programType = $this->dao->getByCode('program_type', $programTypeCode);
	            	$accommodationTypeCode = AccommodationTypeEntity::$CODE_WITHOUT;
	            	if ($programType['with_accommodation']) {
	            		$accommodationTypeCode = AccommodationTypeEntity::$CODE_FLOOR;
	            	}
	            	
	            	// Compose a category code
	            	$accommodationType = $this->dao->getByCode('accomodation_type', $accommodationTypeCode);
	            	$categoryCode = $programType['code_part'] . $batchType['code_part'] . $accommodationType['code_part'];
	            	
	            	$category = array(
	            			'seminar_id' => $seminarId,
	            			'accomodation_type_code' => $accommodationTypeCode,
	            			'batch_type_code' => constants::$singleDayBatchTypeCode,
	            			'program_type_code' => $programType['code'],
	            			'category_code'	=> $categoryCode
	            	);
	            	$this->dao->insert('category', $category);
	                foreach ($this->data->priceGroupList as $priceGroupIndex => $priceGroup) {
	                    $categoryPrice = array(
	                    		'seminar_id' => $seminarId,
	                    		'price_group_id' => $priceGroup['id'],
	                    		'category_code' => $categoryCode,
	                    		'price' => $prices[$priceGroupIndex]
	                    );
	                    $this->dao->insert('category_price', $categoryPrice);
	                }
	            }
            }

            // Save the social discount
            if ($this->data->discountForm['socialDiscountEnabled']) {
                $socialDiscount = array(
                    'seminar_id' => $seminarId,
                    'before_deadline_only' => $this->data->discountForm['socialBeforeDeadlineOnly'],
                    'available_for_karmayoga' => $this->data->discountForm['socialAvailableForKarmayoga'],
                    'percentage' => $this->data->discountForm['socialPercentage']
                );
                $this->dao->insert('seminar_social_discount', $socialDiscount);
            }

            // Save the extra charges for late registration
            if ($this->data->discountForm['earlyDiscountEnabled']) {
                $earlyDiscount = array(
                    'seminar_id' => $seminarId,
                    'applies_to_karmayoga' => $this->data->discountForm['earlyDiscountAppliesToKarmayoga'],
                    'percentage' => $this->data->discountForm['earlyDiscountPercentage']
                );
                $this->dao->insert('seminar_early_discount', $earlyDiscount);
            }
            
            // Save the late registration charge
            if ($this->data->chargesForm['lateChargeEnabled']) {
            	$earlyDiscount = array(
            			'seminar_id' => $seminarId,
            			'applies_to_karmayoga' => $this->data->chargesForm['lateChargeAppliesToKarmayoga'],
            			'applies_to_single_days' => $this->data->chargesForm['lateChargeAppliesToShortPrograms'],
            			'percentage' => $this->data->chargesForm['lateChargePercentage']
            	);
            	$this->dao->insert('seminar_late_charge', $earlyDiscount);
            }

            // Save the extra charges for cancellation
            if ($this->data->chargesForm['cancellationChargeEnabled']) {
                $cancellationCharge = array(
                    'seminar_id' => $seminarId,
                    'applies_to_karmayoga' => $this->data->chargesForm['cancellationAppliesToKarmayoga'],
                    'percentage_before_deadline' => $this->data->chargesForm['cancellationChargeBeforeDeadline'],
                    'percentage_after_deadline' => $this->data->chargesForm['cancellationChargeAfterDeadline']
                );
                $this->dao->insert('seminar_cancellation_charge', $cancellationCharge);
            }

            $this->dao->getDb()->commit();
        }
        catch (Exception $e) {
            // If something raised an exception in our transaction block, roll it back.
            $this->dao->getDb()->rollBack();

            // If there are some recoverable cases, handle theme here.
            
            // Clear the wizard data
            // $this->clearData();

            throw $e;
        }

        $this->clearData();
        $this->redirect('/admin/seminarWizard/success?id=' . $seminarId);
    }

    public function success() {
        $seminarId = $this->input->get('id');
        $this->contentLayout = 'admin/adminLayout';
        $this->renderSubview('admin/seminarWizard/wizardSuccess');
    }
}
